#pragma once 
#include<iostream>
#include<queue>
#include<unistd.h>
using namespace std;

//这是一个线程池的实现，采用的是生产者消费者模型，线程池中有多个线程，当任务队列为空时，消费者线程（线程池中的线程）会等待
//   直到生产者（外部线程）往任务队列中加入新的任务；
//当任务队列满时，生产者线程会等待，直到消费者线程取走任务，空出空间。
//在这个实现中，线程池中的最大线程数量被设置为5个（即count=5），并且任务队列的最多任务个数被设置为10个（即capacity=10）。
const int count  = 5; //线程池中的最大线程数量
template<class T>
class ThreadPool
{
    public:
        ThreadPool(int tc = count)//tc: 线程池中的线程数量
        { 
            pthread_mutex_init(&lock_, NULL);
            pthread_cond_init(&cons_cond_, NULL);
            pthread_cond_init(&prod_cond_, NULL);

            thread_count_ = tc;
            capacity_ = 10; //最多可以有多少个任务

            exit_flag_ = 0; 
        }

        ~ThreadPool()
        {
            pthread_mutex_destroy(&lock_);
            pthread_cond_destroy(&cons_cond_);
            pthread_cond_destroy(&prod_cond_);
        }

        //初始化线程池
        /*
         * 1. 创建线程池的线程
         */
int InitThreadPool() //返回值:成功创建的线程个数
{
    /*默认的属性为joinable属性， 需要其他线程进行pthread_join*/
    /*后期让线程自己把自己分离掉， 不需要其他线程进行回收资源*/
    int exit_thread_count = 0; //创建失败的线程个数
    for(int i = 0; i < thread_count_; i++)
    {
        pthread_t tid;
        // 创建线程，并将线程池对象的指针作为参数传递给线程,以便在线程中访问线程池对象的非静态成员变量和成员函数
        int ret = pthread_create(&tid, NULL, thread_pool_start, (void*)this);
        if(ret < 0){
            exit_thread_count++;
        }
     }

    return thread_count_ -= exit_thread_count; /*实际创建出来的线程个数*/
}

/*
    * 1.这个是线程入口函数， 线程池当中的线程都执行的是该函数
    * 2.static的运行， 防止传递this指针， 满足pthread_create对线程入口函数参数的要求
    * 3.创建线程的时候， 将this指针通过入口函数的参数传递进来
    * */
static void* thread_pool_start(void* arg)
{
    //由于静态成员函数没有this指针,所以在类里面不能返回类的非静态成员函数/成员
    // err: 直接使用&prod_cond_  正解:&tp->prod_cond_
    //线程的例程函数的参数为this指针,通过这个指针访问非静态成员函数/成员
    pthread_detach(pthread_self());
    ThreadPool* tp = (ThreadPool*)arg;
    while(1)
    {
        pthread_mutex_lock(&tp->lock_);
        while(tp->que_.empty())  //如果队列为空，则当前线程会等待，直到有新任务加入队列或者线程池退出
        { 
            if(tp->exit_flag_)// 如果退出标志被设置，那么线程退出，线程数量减一
            { 
                pthread_mutex_unlock(&tp->lock_);
                tp->thread_count_--;
                cout << pthread_self() << " exit...." << endl;
                pthread_exit(NULL); //终止当前线程, 参数为:退出信息
            }
            //在cons_cond_条件变量下等待
            pthread_cond_wait(&tp->cons_cond_, &tp->lock_);
        }

        T task;
        tp->Pop(&task);

        pthread_mutex_unlock(&tp->lock_);
        //通知生产者进行生产(提供任务)
        pthread_cond_signal(&tp->prod_cond_);//唤醒在prod_cond_等待的线程
        task.Run(); //100s
    }
    return NULL;
}


void Push(const T& task)
{
    pthread_mutex_lock(&lock_);
    if(exit_flag_) //退出标志位是否被设置
    {
        pthread_mutex_unlock(&lock_);  //此时就不能push任务,返回
        return;
    }
    while(que_.size() >= capacity_)  //任务队列当中的任务太多,超过了限制
    { 
        pthread_cond_wait(&prod_cond_, &lock_);	//外部线程在prod_cond_条件下进行等待
    }

    que_.push(task); //push任务
    pthread_mutex_unlock(&lock_);
    pthread_cond_signal(&cons_cond_);
}

void Pop(T* qd){
    *qd = que_.front();
    que_.pop();
}

//销毁线程池当中的线程
void ThreadPoolExit(){
    exit_flag_ = 1;

    /*method 1*/
    while(thread_count_ > 0)
    {
        pthread_cond_signal(&cons_cond_); //唤醒等待拿任务的线程池中的线程
    }

    /*method 2*/
    //pthread_cond_broadcast(&cons_cond_);
}
    private:
        std::queue<T> que_;
        size_t capacity_; //任务队列的最多的任务个数
        //互斥
        pthread_mutex_t lock_; //对任务队列加锁保护 确保同一时间只有一个线程对队列进行操作
        //同步

        //消费者线程等待的条件变量
        pthread_cond_t cons_cond_;//当队列为空时，等待任务队列中有任务可以消费 线程池中的线程会等待 cond_cond 信号
		//在消费者线程中，如果任务队列为空，线程将会阻塞并等待cons_cond_的信号，以便被唤醒后进行消费任务。
		
        //生产者线程等待的条件变量
		//prod_cond_用于当任务队列已满时，等待任务队列中有位置可以添加任务。
		//在生产者线程中，如果任务队列已满，线程将会阻塞并等待prod_cond_的信号，以便被唤醒后添加任务到队列中。
        pthread_cond_t prod_cond_;//当任务太多,外部线程在prod_cond_条件下进行等待

        int thread_count_;//提供服务的线程个数

        //退出标志位 ： 表示线程池是否需要退出 1：退出  0：不退出
        int exit_flag_;
};

// 在生产者添加任务时，会通过pthread_cond_signal函数唤醒等待在cons_cond_上的一个线程，
// 如果有多个线程在等待，那么只会唤醒其中一个线程，其他线程仍然会继续等待。
// 在消费者处理完任务后，会通过pthread_cond_signal函数唤醒等待在prod_cond_上的一个线程，以便生产者可以继续添加任务

// cons_cond_ 和 prod_cond_ 都是用来实现线程间同步的条件变量。

// 具体而言：

// cons_cond_：当任务队列为空时，消费者线程会等待 cons_cond_ 条件变量。
// 当生产者向队列中添加元素后，生产者线程会发出信号（使用 pthread_cond_signal 或 pthread_cond_broadcast）通知消费者线程继续执行。

// prod_cond_：当任务队列已满时，生产者线程会等待 prod_cond_ 条件变量。
// 当消费者从队列中取出元素后，消费者线程会发出信号通知生产者线程继续添加元素。

// 这样可以确保线程池中的消费者线程只在有任务时才进行处理，避免浪费 CPU 资源；
// 同时，生产者线程只在队列未满时才添加任务，避免队列溢出。通过这两个条件变量的协作，可以保证线程池的高效和稳定。
